1 module hip.util.sumtype;
2 
3 enum Type
4 {
5     undefined,
6     void_,
7     object,
8     string_,
9     bool_,
10     //Signed
11     byte_,
12     short_,
13     int_,
14     long_,
15     //Unsigned
16     ubyte_,
17     ushort_,
18     uint_,
19     ulong_,
20     //Floating
21     float_,
22     double_,
23     ///Untested
24     real_
25 }
26 
27 private union TypeUnion
28 {
29     string string_;
30     void* void_;
31     Object object;
32     bool bool_;
33     //Signed
34     byte byte_;
35     short short_;
36     int int_;
37     long long_;
38     //Unsigned
39     ubyte ubyte_;
40     ushort ushort_;
41     uint uint_;
42     ulong ulong_;
43     //Floating
44     float float_;
45     double double_;
46     ///Untested
47     real real_;
48 }
49 pure Type getType(T)()  nothrow @nogc @safe
50 {
51     with(Type)
52     {
53         static if(is(T == string))
54             return string_;
55         else static if(is(T == void*))
56             return void_;
57         else static if(is(T : Object))
58             return object;
59         else static if(is(T == bool))
60             return bool_;
61         else static if(is(T == byte))
62             return byte_;
63         else static if(is(T == short))
64             return short_;
65         else static if(is(T == int))
66             return int_;
67         else static if(is(T == long))
68             return long_;
69         else static if(is(T == ubyte))
70             return ubyte_;
71         else static if(is(T == ushort))
72             return ushort_;
73         else static if(is(T == uint))
74             return uint_;
75         else static if(is(T == ulong))
76             return ulong_;
77         else static if(is(T == float))
78             return float_;
79         else static if(is(T == double))
80             return double_;
81         else static if(is(T == real))
82             return real_;
83         else static assert(false, "Unimplemented for type "~T.stringof);
84     }
85 }
86 
87 /**
88 *   Use that when you want to hold arbitrary defined type (which usually must be converted)
89 *   By using sumtype, your data will be converted only once and after that, it will be runtime
90 *   type strict.
91 */
92 struct Sumtype
93 {
94     Type type = Type.undefined;
95     TypeUnion data = void;
96 
97     string getTypeName() const @nogc
98     {
99         switch(type)
100         {
101             static foreach(mem; __traits(allMembers, Type))
102             {
103                 case __traits(getMember, Type, mem):
104                     return mem[0..$-1];
105             }
106             default:
107                 return "undefined";
108         }
109     }
110 
111     T get(T)() const
112     {
113         if(getType!T != type)
114             throw new Exception("Tried to get a mismatching type: "~T.stringof~" (expected "~getTypeName~")");
115         return *cast(T*)(cast(void*)&data);
116     }
117     T set(T)(T value)
118     {
119         if(type == Type.undefined)
120             this.type = getType!T;
121         else if(type != getType!T)
122             throw new Exception("Tried to get a mismatching type: "~T.stringof~" (expected "~getTypeName~")");
123         data = *cast(TypeUnion)(cast(void*)&data);
124         return value;
125     }
126 
127     T opCast(T)() const
128     {
129         return get!T;   
130     }
131     bool opBinary(string op : "in")(const Type t) const
132     {
133         return type == t;
134     }
135     alias opBinaryRight = opBinary;
136 
137     static Sumtype make(string data)
138     {
139         return Sumtype(Type.string_, cast(TypeUnion)data);
140     }
141     
142     static Sumtype make(T)(T data) if(!is(T == string))
143     {
144         Sumtype s = void;
145         s.type = getType!T;
146         s.data = *cast(TypeUnion*)(cast(void*)&data);
147         return s;
148     }
149 
150     static Sumtype make(T)(string data)
151     {
152         import hip.util.conv:to;
153         return make!T(to!T(data));
154     }
155 }